package com.own.stu.apm.agent.core.plugin;

import com.own.stu.apm.agent.core.plugin.match.ClassMatch;
import com.own.stu.apm.agent.core.plugin.match.IndirectMatch;
import com.own.stu.apm.agent.core.plugin.match.NameMatch;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

import java.util.*;

import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.not;

public class PluginFinder {

    /**
     * 用于存储classMatch类型为NameMatch的插件
     * key:全类名,如:com.roadjava.skywalking.agent.demo.app.service.UserInfoService*value: 说明同一个类可以同时被多个plugin进行增强
     */
    private final Map<String, LinkedList<AbstractClassEnhencePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhencePluginDefine>>();
    /**
     * 用于存储ClassMatch类型为IndirectMatch匹现器的容器
     */
    private final List<AbstractClassEnhencePluginDefine> signatureMatchDefine = new ArrayList<>();

    public PluginFinder(List<AbstractClassEnhencePluginDefine> plugins) {
        for (AbstractClassEnhencePluginDefine plugin : plugins) {

            ClassMatch classMatch = plugin.enhanceClass();
            if (classMatch == null) {
                continue;
            }

            if (classMatch instanceof NameMatch) {
                NameMatch nameMatch = (NameMatch) classMatch;
                LinkedList<AbstractClassEnhencePluginDefine> list = nameMatchDefine.computeIfAbsent(nameMatch.getClassName(), a -> new LinkedList<>());
                list.add(plugin);
            } else {
                signatureMatchDefine.add(plugin);
            }
        }
    }

    public ElementMatcher<? super TypeDescription> buildMatch() {
        ElementMatcher.Junction<? super TypeDescription> junction = new ElementMatcher.Junction.AbstractBase<NamedElement>() {
            @Override
            public boolean matches(NamedElement target) {
                return nameMatchDefine.containsKey(target.getActualName());
            }
        };

        junction = junction.and(not(isInterface()));
        for (AbstractClassEnhencePluginDefine pluginDefine : signatureMatchDefine) {

            ClassMatch classMatch = pluginDefine.enhanceClass();
            if (!(classMatch instanceof IndirectMatch)) {
                continue;
            }
            IndirectMatch indirectMatch = (IndirectMatch) classMatch;
            junction = junction.or(indirectMatch.buildJunction());
        }
        return junction;
    }

    public LinkedList<AbstractClassEnhencePluginDefine> find(TypeDescription typeDescription) {
        LinkedList<AbstractClassEnhencePluginDefine> result = new LinkedList<AbstractClassEnhencePluginDefine>();
        String typeName = typeDescription.getTypeName(); // 全类名
        LinkedList<AbstractClassEnhencePluginDefine> pluginDefines = nameMatchDefine.get(typeName);
        if(pluginDefines != null){
            result.addAll(pluginDefines);
        }

        for (AbstractClassEnhencePluginDefine pluginDefine : signatureMatchDefine) {

            IndirectMatch classMatch = (IndirectMatch) pluginDefine.enhanceClass();
            if(classMatch.isMatch(typeDescription)){
                result.add(pluginDefine);
            }
        }

        return result;
    }
}
